﻿
namespace HSS.Xaml.UI.Controls
{
	#region Using Directives
	using System;
	using System.Collections.ObjectModel;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Linq;
	using System.Windows;
	using System.Windows.Controls;
	using HSS.ComponentModel;
	#endregion

	#region PropertyGridEntry
	/// <summary>
	/// PropertyGridEntry represents a Row with a Label and Editor for an Object's Property
	/// </summary>
	public sealed class PropertyGridEntry : DependencyObject, IPropertyGridItem
	{
		#region Fields
		private Collection<PropertyGridEntry> children = new Collection<PropertyGridEntry>();
		private bool IsInPropertySet = false;

		#endregion

		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		public PropertyGridEntry()
		{
		}
		#endregion

		#region Properties

		/// <summary>
		/// Gets the <see cref="HSSPropertyDescriptor"/> for this entry
		/// </summary>
		internal HSSPropertyDescriptor Descriptor { get; set; }
		/// <summary>
		/// Gets or sets the PropertyGrid associated with this Item.
		/// </summary>
		internal PropertyGrid PropertyGrid
		{
			get { return _propertyGrid; }
			set { _propertyGrid = value; }
		} PropertyGrid _propertyGrid;
		/// <summary>
		/// Gets the current value
		/// </summary>
		internal object CurrentValue
		{
			get
			{
				return _currentValue;
			}
			private set
			{
				_currentValue = value;

				var inpc = this.Instance as INotifyPropertyChanged;
				if (null != inpc)
					inpc.PropertyChanged += this.Value_PropertyChanged;

				inpc = this._currentValue as INotifyPropertyChanged;
				if (null != inpc)
					inpc.PropertyChanged += this.Value_PropertyChanged;

				inpc = value as INotifyPropertyChanged;
				if (null != inpc)
					inpc.PropertyChanged += this.Value_PropertyChanged;
			}
		} object _currentValue;
		/// <summary>
		/// Gets the Entry's Margin
		/// </summary>
		internal Border MarginColumn { get; private set; }
		/// <summary>
		/// Gets the PropertyGridLabelControl
		/// </summary>
		internal PropertyGridLabelControl LabelControl { get; private set; }
		/// <summary>
		/// Gets the ValueControl for this entry.
		/// </summary>
		internal PropertyGridValueControl ValueControl { get; private set; }
		/// <summary>
		/// Gets the Editor Control
		/// </summary>
		internal PropertyGridEditor Editor { get; private set; }

		#region IPropertyGridItem
		/// <summary>
		/// Gets the ordinal index of this editor within the property grid.
		/// </summary>
		public int RowIndex { get; set; }
		/// <summary>
		/// Gets or sets whether this Entry is selected
		/// </summary>
		public bool IsSelected { get; set; }
		/// <summary>
		/// Gets or sets if this Entry is active
		/// </summary>
		public bool IsActive { get; set; }
		/// <summary>
		/// Gets the Category of this entry
		/// </summary>
		public string Category { get; set; }
		/// <summary>
		/// Gets the Name of this entry
		/// </summary>
		public string Name { get; set; }
		/// <summary>
		/// Gets the DisplayName of this entry
		/// </summary>
		public string DisplayName { get; set; }
		/// <summary>
		/// Gets the Description of this entry
		/// </summary>
		public string Description { get; set; }
		/// <summary>
		/// Get if this Entry is visible or collapsed
		/// </summary>
		public bool IsVisible
		{
			get { return _isVisible; }
			set
			{
				_isVisible = value;
				if (_isVisible)
				{
					this.MarginColumn.Visibility = Visibility.Visible;
					this.LabelControl.Visibility = Visibility.Visible;
					this.ValueControl.Visibility = Visibility.Visible;
				}
				else
				{
					this.MarginColumn.Visibility = Visibility.Collapsed;
					this.LabelControl.Visibility = Visibility.Collapsed;
					this.ValueControl.Visibility = Visibility.Collapsed;
				}
			}
		} bool _isVisible = true;
		/// <summary>
		/// Get if this Entry's editor is focused regardless if the editor is in edit mode or readonly mode
		/// </summary>
		public bool IsEditorFocused { get; set; }
		/// <summary>
		/// Gets or sets the Instance object
		/// </summary>
		public object Instance { get; set; }
		#endregion

		#endregion

		#region Methods

		#region IPropertyGridItem
		/// <summary>
		/// Select this item, activate the Editor and Execute the default action for the Editor
		/// </summary>
		public void ExecuteDefaultAction()
		{
			if (!this.Descriptor.IsReadOnly)
			{
				this.Editor.PrepareExecuteDefaultAction();
				this.Editor.BeginEdit();
				this.Editor.ExecuteDefaultAction();
			}
		}
		/// <summary>
		/// Select this item
		/// </summary>
		public void Select()
		{
			this.IsSelected = true;
			this.UpdateLabelState();
			if (this.IsActive)
			{
				if (this.IsEditorFocused && !this.Descriptor.IsReadOnly)
				{
					this.Editor.BeginEdit();
				}
			}
		}
		/// <summary>
		/// Deselect this item
		/// </summary>
		public void Deselect()
		{
			this.IsSelected = false;
			if (null != this.LabelControl)
			{
				this.LabelControl.IsTabStop = false;
				this.UpdateLabelState();
			}
			if (null != this.Editor)
			{
				if (this.Editor.SupportsEditDialog)
					this.ValueControl.ShowDialogEditButton(false);
				if (this.Editor.IsEditing)
					this.Editor.EndEdit();
			}
			this.IsActive = false;
		}
		/// <summary>
		/// Activate this item
		/// </summary>
		public void Activate()
		{
			if (!this.IsActive)
			{
				this.IsActive = true;
				this.LabelControl.IsTabStop = true;
				this.LabelControl.Focus();
				if (this.Editor.SupportsEditDialog)
					this.ValueControl.ShowDialogEditButton(true);
			}
			this.PropertyGrid.SelectItem(this);
		}
		/// <summary>
		/// Clean up instance state
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		public void OnRemoved()
		{
			try
			{
				this.Deselect();
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex.ToString());
			}
			try
			{
				this.Editor = null;
				this.LabelControl = null;
				if (null != this.Descriptor)
				{
					this.Descriptor.ValueError -= new EventHandler<ExceptionEventArgs>(Descriptor_ValueError);
					this.Descriptor = null;
				}
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex.ToString());
			}
		}
		#endregion

		/// <summary>
		/// Initialize the PropertyGridEntry.
		/// </summary>
		/// <returns>true if the initialization was successful; otherwise false.</returns>
		public bool Initialize()
		{
			if (null == this.Descriptor)
				return false;

			this.Descriptor.ValueError += new EventHandler<ExceptionEventArgs>(Descriptor_ValueError);

			this.Editor = this.Descriptor.GetEditor() as PropertyGridEditor;
			if (null == this.Editor)
				return false;

			var value = this.Descriptor.GetValue(this.Instance);
			this.CurrentValue = value;
			this.CreateValueControl();
			this.MarginColumn = new Border
			{
				Background = this.PropertyGrid.Background
			};
			this.Editor.PropertyEntry = this;
			this.Editor.Initialize(value);

			this.CreateLabel();
			if (null == this.LabelControl)
			{
				this.Editor = null;
				return false;
			}

			return true;
		}
		/// <summary>
		/// Get the current value from the Property Descriptor.
		/// </summary>
		public void Refresh()
		{
			if (null == this.Descriptor || null == this.Editor)
				return;
			var propertyValue = this.GetPropertyValue();
			if (this.IsDifferentValue(propertyValue))
			{
				this.CurrentValue = propertyValue;
				this.Editor.Object = propertyValue;
			}
		}

		internal void EndEdit()
		{
			this.IsEditorFocused = false;
			if (null != this.LabelControl)
				this.LabelControl.Focus();
		}
		internal object GetPropertyValue()
		{
			return this.Descriptor.GetValue(this.Instance);
		}
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
		internal void ResetPropertyValue()
		{
			object value = this.GetPropertyValue();
			this.CurrentValue = value;
			this.Dispatcher.BeginInvoke(() =>
			{
				this.Refresh();
			});
		}
		internal void SetPropertyValue(object value)
		{
			if (!ControlUtil.AreValuesEqual(this.CurrentValue, value))
			{
				if (!this.Descriptor.IsReadOnly)
				{
					this.IsInPropertySet = true;
					try
					{
						this.Descriptor.SetValue(this.Instance, value);
					}
					finally
					{
						this.IsInPropertySet = false;
					}
				}

				Dispatcher.BeginInvoke(() =>
				{
					this.Refresh();
				});
			}
		}

		private void UpdateLabelState()
		{
			this.LabelControl.UpdateVisualState(this.IsSelected, this.IsActive, !!this.Descriptor.IsReadOnly);
		}
		private bool IsDifferentValue(object newValue)
		{
			return !ControlUtil.AreValuesEqual(this.CurrentValue, newValue);
		}
		private void CreateValueControl()
		{
			var control = new PropertyGridValueControl()
			{
				PropertyGrid = this.PropertyGrid
			};
			control.Initialize(this);
			this.ValueControl = control;
		}
		private void CreateLabel()
		{
			string displayName = this.Descriptor.DisplayName;
			var para = this.Descriptor.GetAttribute<ParenthesizePropertyNameAttribute>();
			if (para != null && para.NeedParenthesis)
				displayName = "(" + displayName + ")";

			this.LabelControl = new PropertyGridLabelControl()
			{
				LabelText = displayName
			};
			this.LabelControl.Initialize(this);
		}

		#endregion

		#region Event Handlers
		private void Descriptor_ValueError(object sender, ExceptionEventArgs e)
		{
			this.Editor.ShowError(e.Exception);
		}
		private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (this.children != null)
			{
				var item = this.children.FirstOrDefault(pi => pi.Descriptor.Name == e.PropertyName);
				if ((item != null) && !item.IsInPropertySet)
				{
					item.Refresh();
				}
			}
		}
		#endregion
	}
	#endregion
}
